#ifndef _FENGINE_NODE
#define _FENGINE_NODE

#include "FEngineType.h"
#include <list>
#include <string>

namespace FEngine
{
	/**
	 * A Node is an element of the scene graph tree.
	 * Each Node has one parent Node and can have a unlimited number of child.
	 * A Node has a position, a rotation and a scale which represent him in the scene.
	 * The virtual methods update(double) and render(double) are called each frame of
	 * the main loop and are used to update the logic and to send the render data to directX.
	 */
	class Node
	{
		private:

			Node*				mParentNode;
			std::list<Node*>	mChildNodeList;
			
			Vector3				mPosition;
			Vector3				mLocalScale;	// Does not spread to the children
			Vector3				mScale;
			Vector3				mRotation;		// Angles in degree
			
			D3DXMATRIX			mWorldMatrix;	// Matrix of the world space
			D3DXMATRIX			mLocalMatrix;	// Matrix of the model space
			bool				mHasLocalMatrixChanged;
			bool				mHasWorldMatrixChanged;


		public:
			
			/**
			 * Constructor.
			 */
			Node();
			virtual ~Node();
			
			/**
			 * Add another Node to the list of child of this Node.
			 * @param _node Child node to add.
			 */
			void addChild(Node* _node);

			void _addedToParentNode();
			
			/**
			 * Remove one of the child Node from the list of child of this node.
			 * @param _node Child node to remove.
			 */
			void removeChild(Node* _node);
			
			/**
			 * Update the logic of the Node.
			 * Called every frame.
			 * @param _elpsedTime Time elapsed since the last frame.
			 */
			virtual void update(double _elapsedTime);
			
			/**
			 * Send the render data of the Node to directX.
			 * Called every frame.
			 * @param _elpsedTime Time elapsed since the last frame.
			 */
			virtual void render(double _elapsedTime);

			/**
			 * Return the parent Node to wich is attached the Node.
			 * @return Parent Node or NULL if the Node has no parent.
			 */
			inline Node* getParentNode() const
			{
				return mParentNode;
			}

			void _setParentNode(Node* _node);
			
			/**
			 * Return the position of the Node in the World space.
			 * @return Vector3 containing the position of the Node.
			 */
			Vector3 getPositionWorld();

			/**
			 * Return the position of the Node in the Local space.
			 * @return Vector3 containing the position of the Node.
			 */
			inline Vector3 getPosition() const
			{
				return mPosition;
			}

			/**
			 * Set the position of the Node in Local space.
			 * @param _pos Vector3 containing the position of the Node.
			 */
			void setPosition(const Vector3& _pos);

			/**
			 * Set the position of the Node in Local space.
			 * @param _x Position of the Node on the X axis.
			 * @param _y Position of the Node on the Y axis.
			 * @param _z Position of the Node on the Z axis.
			 */
			void setPosition(float _x, float _y, float _z);
			
			/**
			 * Translate the position of the Node in Local space.
			 * @param _pos Vector3 containing the translation value.
			 */
			void translate(const Vector3& _pos);

			/**
			 * Translate the position of the Node in Local space.
			 * @param _x Translation of the Node on the X axis.
			 * @param _y Translation of the Node on the Y axis.
			 * @param _z Translation of the Node on the Z axis.
			 */
			void translate(float _x, float _y, float _z);
			
			/**
			 * Return the scale of the Node.
			 * @return Vector3 containing the scale of the Node.
			 */
			inline Vector3 getScale() const
			{
				return mScale;
			}

			/**
			 * Set the scale of the Node.
			 * @param _pos Vector3 containing the scale of the Node.
			 */
			void setScale(const Vector3& _scale);

			/**
			 * Set the scale of the Node.
			 * @param _x Scale of the Node on the X axis.
			 * @param _y Scale of the Node on the Y axis.
			 * @param _z Scale of the Node on the Z axis.
			 */
			void setScale(float _x, float _y, float _z);

			/**
			 * Set the scale of the Node.
			 * @param _scale Scale of the Node on each axis.
			 */
			void setScale(float _scale);

			/**
			 * Multiplies the scale of the Node by the new value.
			 * @param _pos Vector3 containing the scale factor of the Node.
			 */
			void scale(const Vector3& _scale);

			/**
			 * Multiplies the scale of the Node by the new value.
			 * @param _x Scale factor of the Node on the X axis.
			 * @param _y Scale factor of the Node on the Y axis.
			 * @param _z Scale factor of the Node on the Z axis.
			 */
			void scale(float _x, float _y, float _z);

			/**
			 * Multiplies the scale of the Node by the new value.
			 * @param _scale Scale factor of the Node on each axis.
			 */
			void scale(float _scale);
			
			/**
			 * Get the local scale of the Node.
			 * The local scale is not applied to the childs Node.
			 * @return Vector3 containing the local scale of the Node.
			 */
			inline Vector3 getLocalScale() const
			{
				return mLocalScale;
			}

			/**
			 * Set the local scale of the Node.
			 * The local scale is not applied to the childs Node.
			 * @param _pos Vector3 containing the local scale of the Node.
			 */
			void setLocalScale(const Vector3& _scale);

			/**
			 * Set the local scale of the Node.
			 * The local scale is not applied to the childs Node.
			 * @param _x Local scale of the Node on the X axis.
			 * @param _y Local scale of the Node on the Y axis.
			 * @param _z Local scale of the Node on the Z axis.
			 */
			void setLocalScale(float _x, float _y, float _z);

			/**
			 * Set the local scale of the Node.
			 * @param _scale Local scale of the Node on each axis.
			 */
			void setLocalScale(float _scale);
			
			/**
			 * Return the rotation of the Node in the Local space.
			 * @return Vector3 containing the rotation of the Node.
			 */
			Vector3 getRotation() const
			{
				return mRotation;
			}

			/**
			 * Set the rotation of the Node in Local space.
			 * @param _pos Vector3 containing the rotation of the Node.
			 */
			void setRotation(const Vector3& _rot);

			/**
			 * Set the rotation of the Node in Local space.
			 * @param _x Rotation of the Node on the X axis.
			 * @param _y Rotation of the Node on the Y axis.
			 * @param _z Rotation of the Node on the Z axis.
			 */
			void setRotation(float _x, float _y, float _z);

			/**
			 * Rotate the Node in Local space.
			 * @param _pos Vector3 containing the rotation of the Node.
			 */
			void rotate(const Vector3& _rot);

			/**
			 * Rotate the Node in Local space.
			 * @param _x Rotation of the Node on the X axis.
			 * @param _y Rotation of the Node on the Y axis.
			 * @param _z Rotation of the Node on the Z axis.
			 */
			void rotate(float _x, float _y, float _z);
			

		protected:

			Matrix4 _getWorldMatrix();


		private:

			Node(Node const&);
			void operator=(Node const&);
			
			void _calculateLocalMatrix();
			void _calculateWorldMatrix();
			void _notifyWorldMatrixChanged();


	//	public:
	//		std::string _printTree();
	};

} // namespace FEngine

#endif
